/*  Copyright © 2009 Blake Hegerle -*- C++ -*- */

/*  This file is part of metal.
 
    Metal is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see
    <http://www.gnu.org/licenses/>.

*/

#pragma once

#include <type_traits>

#include "../forward.h"
#include "../decl.h"
#include "../def.h"
#include "function.h"
#include "binding.h"
#include "set.h"
#include "type_check.h"
#include "type_promotion.h"
#include "def_type_check.h"
#include "param.h"

namespace metal {
  namespace meta {
    template<typename... Args>
    struct bind_parameters;
    
    template<
      typename Scope, 
      typename Definitions, 
      typename DeclType, 
      typename Ident,
      typename... Args
    >
    struct module_declaration_binder {
      typedef typename decl_signature<DeclType, Args...>::type signature;

      // compatible == true if first decl, or if signature is the
      // same. 
      typedef typename get<Ident, Scope>::type prev;
      static const bool compatible = std::is_void<prev>::value
	|| std::is_same<prev, signature>::value;

      typedef typename bind<Ident, signature, Scope>::type new_scope;

      typedef module<new_scope, Definitions> type;
    };
    
    template<
      typename Scope, 
      typename Definitions, 
      typename Ident, 
      typename MetaExpr,
      typename... Args
    >
    struct module_definition_binder {
      typedef definition_type_check<
	Scope, 
	MetaExpr,
	Args...
      > check;

      typedef typename check::type return_type;

      typedef module_declaration_binder<
	Scope, 
	Definitions, 
	return_type,
	Ident, 
	Args...
      > decl;

      static const bool duplicate = contained<Ident, Definitions>::value;
      static const bool is_safe = !duplicate && check::is_safe;
      static const bool is_bound = check::is_bound;
      static const bool compatible = decl::compatible;
      
      typedef typename decl::signature signature;
      typedef typename decl::new_scope new_scope;
      
      typedef module<
	new_scope,
	typename set_add<Ident, Definitions>::type
      > type;
    };

    template<
      typename Scope, 
      typename Definitions, 
      typename Type,
      typename Ident, 
      typename MetaExpr,
      typename... Args
    >
    struct module_recursive_definition_binder {
      typedef typename decl_signature<Type, Args...>::type signature;

      typedef typename bind<Ident, signature, Scope>::type new_scope;
      
      typedef definition_type_check<
	new_scope, 
	MetaExpr,
	Args...
      > check;
      
      typedef Type return_type;
      
      typedef module_declaration_binder<
	new_scope, 
	Definitions, 
	Type,
	Ident, 
	Args...
      > decl;
      
      static const bool duplicate = contained<Ident, Definitions>::value;
      static const bool is_safe = !duplicate 
	&& check::is_safe
	&& allow_conversion<typename check::type, Type>::value;
      static const bool is_bound = check::is_bound;
      static const bool compatible = decl::compatible;
      
      typedef module<
	new_scope,
	typename set_add<Ident, Definitions>::type
      > type;
    };

    template<>
    struct bind_parameters<> {
      typedef bindings<> type;
    };

    template<typename T, typename I, typename... Args>
    struct bind_parameters<parameter<T, I>, Args...> {
      typedef bind_parameters<Args...> next;

      typedef typename bind<I, T, typename next::type>::type type;
    };
      }
}
